package com.sky.aspect;

import com.fasterxml.jackson.databind.ser.Serializers;
import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 自定义切面类 实现公共字段自动填充处理逻辑
 */
@Aspect   // 指定这是一个切面类
@Component  // 指定交给Bean组件对象
@Slf4j
public class AutoFillAspect  {
    /**
     *  指定切入点的方法
     */
    // 利用 exec来指定起作用的类 利用annotation来指定起作用的具体方法 只有使用了 autoFill注解才会起作用
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){}

    // 指定通知 有多种通知（前置 环绕 后置 异常） 需要在 这个操作（insert update）之前对其赋值
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { // 通过 这个 切入点参数 joinPoint 来获取截取到的方法
        log.info("开始进行 公共字段自动填充");
        /**
         * 这段代码是通过反射机制写的 可读性比较差
         */
        // 获取当前被拦截方法上的数据类型操作 （INSERT UPDATE） 对Signature向下转型 转成 方法

        MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //方法签名对象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); // 获得方法上的注解对象
        OperationType operationType = autoFill.value();  // 获得数据库的操作类型 也就是 注解对象的值

        // 获取 当前被拦截方法的参数 也就是实体对象
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length==0){
            return;
        }
        Object entity = args[0]; // 获取参数 第一个参数 因为不确定是哪一种类型 因此 要指定 object

        // 准备赋值的数据
        LocalDateTime now = LocalDateTime.now();
        Long currendId = BaseContext.getCurrentId();

        // 根据当前的不同操作类型 为对应的属性通过反射来进行赋值
        if (operationType == OperationType.INSERT){
            // 为4个公共字段进行赋值
            Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
            Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
            Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
            Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);


            // 通过反射机制 为对象赋值
            setCreateUser.invoke(entity,currendId);
            setUpdateUser.invoke(entity,currendId);
            setCreateTime.invoke(entity,now);
            setUpdateTime.invoke(entity,now);
        }else if (operationType == OperationType.UPDATE){
            // 为2个公共字段进行赋值
            Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
            Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

            setUpdateUser.invoke(entity,currendId);
            setUpdateTime.invoke(entity,now);
        }

    }

}
